#include "global.h"
#include "dynamax.h"
#include "battle.h"
#include "battle_evolution.h"
#include "battle_interface.h"
#include "battle_ai_script_commands.h"
#include "battle_scripts.h"
#include "item.h"
#include "constants/moves.h"
#include "random.h"
#include "constants/species.h"
#include "battle_anim.h"

extern u8 BattleScript_DynamaxPrintAuroraVeil[];
extern u8 BattleScript_DynamaxRecycleItem[];
extern u8 BattleScript_DynamaxSetTealthrock[];
extern u8 BattleScript_DynamaxHealSelfAll[];
extern u8 BattleScript_DynamaxTryppreduce[];
extern u8 BattleScript_DynamaxHealPartyStatus[];
extern u8 BattleScript_DynamaxSetTorment[];
extern u8 BattleScript_DynamaxTryinfatuating[];
extern u8 BattleScript_DynamaxEffectNotDoneYet[];

extern const u8 gDynamaxMovePowers[];

void DoZMove(u32 battlerId);
void GigaMaxReplaceMove(u32 battlerId);
bool8 BattleEndTurnFormChange(u32 battlerId);

void HideTriggerSprite(void)
{
    HideMegaTriggerSprite();
    gBattleStruct->mega.playerSelect = FALSE;
    ChangeTriggerSprite(gActiveBattler, 0);
}

u8 CreateIndicator(u32 battlerId)
{
    u8 type = GetEvolutionType(battlerId);
    u8 spriteId = GetIndicatorSpriteId(gHealthboxSpriteIds[battlerId]);
    if (spriteId != 0xFF)
        return spriteId;
    if (type == EvolutionMega)
        return CreateMegaIndicatorSprite(battlerId);
    else if (type == EvolutionDynamax)
        return CreateDynamaxIndicator(battlerId);
    return 0xFF;
}
extern void MoveSelectionDisplayMoveNames();
void MoveSelectionCreateCursorAt(u8 cursorPos, u8 arg1);
void RefreshMoveMenu(u8 battler, u8 state)
{
    typedef void (*TriggerCallback)(u32 battlerId);
    struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]);
    u8 i;
    u8 evoState = GetEvolutionType(battler);
    if (evoState == EvolutionDynamax)
    {
        static const TriggerCallback gMaxCallback[] = { RestoreMaxMove, GigaMaxReplaceMove };
        gMaxCallback[state & 1](battler);
    }
    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        moveInfo->moves[i] = gBattleMons[battler].moves[i];
    }
    MoveSelectionDisplayMoveNames();
    MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
}

void ChangeTriggerSprite(u8 battler, u8 state)
{
    if (GetEvolutionType(battler) == EvolutionZmove && state)
        gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMoveDisableChangeMove;
    ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, state);
}

u8 GetIndicatorSpriteId(u32 healthboxSpriteId)
{
    return GetMegaIndicatorSpriteId(healthboxSpriteId);
}


const u8* VariousClearTerrian()
{
    if (gFieldStatuses > 0)
    {
        gFieldStatuses &=  ~STATUS_FIELD_TERRAIN_ANY;
        gFieldTimers.grassyTerrainTimer = 0;
        gFieldTimers.mistyTerrainTimer = 0;
        gFieldTimers.electricTerrainTimer = 0;
        gFieldTimers.psychicTerrainTimer = 0;
        gBattleCommunication[MULTISTRING_CHOOSER] = 0;
        return BattleScript_DynamaxClearFieldEffects;
    }
    return NULL;
}

void HandleClearRocks() {
    u8 atkSide = GET_BATTLER_SIDE2(gBattlerAttacker);
	const u8* script;
    if (gSideStatuses[atkSide] & SIDE_STATUS_SPIKES)
    {
        gSideStatuses[atkSide] &= ~(SIDE_STATUS_SPIKES);
        gSideTimers[atkSide].spikesAmount = 0;
        script = BattleScript_SpikesFree;
    }
    else if (gSideStatuses[atkSide] & SIDE_STATUS_TOXIC_SPIKES)
    {
        gSideStatuses[atkSide] &= ~(SIDE_STATUS_TOXIC_SPIKES);
        gSideTimers[atkSide].toxicSpikesAmount = 0;
        script = BattleScript_ToxicSpikesFree;
    }
    else if (gSideStatuses[atkSide] & SIDE_STATUS_STICKY_WEB)
    {
        gSideStatuses[atkSide] &= ~(SIDE_STATUS_STICKY_WEB);
        gSideTimers[atkSide].stickyWebAmount = 0;
        script = BattleScript_StickyWebFree;
    }
    else if (gSideStatuses[atkSide] & SIDE_STATUS_STEALTH_ROCK)
    {
        gSideStatuses[atkSide] &= ~(SIDE_STATUS_STEALTH_ROCK);
        gSideTimers[atkSide].stealthRockAmount = 0;
        script = BattleScript_StealthRockFree;
    }
    else if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)
    {

        script = VariousClearTerrian();
    }
    else if (gSideTimers[BATTLE_OPPOSITE(atkSide)].reflectTimer)
    {
        gSideStatuses[BATTLE_OPPOSITE(atkSide)] &= ~(SIDE_STATUS_REFLECT);
        gSideTimers[BATTLE_OPPOSITE(atkSide)].reflectTimer = 0;
        gBattleCommunication[MULTISTRING_CHOOSER] = 1;
        script = BattleScript_DynamaxClearFieldEffects;
    }
    else if (gSideTimers[BATTLE_OPPOSITE(atkSide)].lightscreenTimer)
    {
        gSideStatuses[BATTLE_OPPOSITE(atkSide)] &= ~(SIDE_STATUS_LIGHTSCREEN);
        gSideTimers[BATTLE_OPPOSITE(atkSide)].lightscreenTimer = 0;
        gBattleCommunication[MULTISTRING_CHOOSER] = 2;
        script = BattleScript_DynamaxClearFieldEffects;
    }
    else return;
    gBattlescriptCurrInstr -= 5;
    BattleScriptPushCurrent(script);
}

void HandleDynamaxChangeTarget()
{
    u32 attacker = gBattleScripting.savedMoveEffect << 1 >> 15 ^ 1;
    //1是自己，0是敌方
    u8* battler = (&gBattlerAttacker) + attacker;//acturally battler target
    u8 battlerId = BATTLE_PARTNER(*battler);
    gBattleScripting.savedBattler = *battler | (attacker << 4);
    if (IsBattlerAlive(battlerId))
    {
        *battler = battlerId;
        gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
    }
}

void DynamaxRestoreTarget()
{
    u8 battler = gBattleScripting.savedBattler & 0x3;
    u8 id = gBattleScripting.savedBattler >> 4;
    (&gBattlerAttacker)[id] = battler;
}

void HandleDynamaxSetMoveEffect()//see move end
{
    u8 arg = gBattleMoves[gCurrentMove].argument;
    const static u8 status_list[] = {MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_TOXIC, MOVE_EFFECT_SLEEP};
    u16 effect = arg;

    if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
        return;

    if ((arg >= MOVE_EFFECT_ATK_PLUS_1 && arg <= MOVE_EFFECT_EVS_PLUS_1)
    	|| arg == DYNAMAX_HEAL_USER)
		effect = (arg | MOVE_EFFECT_AFFECTS_USER);
	else switch (arg)
    {
    case DYNAMAX_RANDOM_STATUS:
        effect = status_list[(Random() % 3)];
        break;
    case DYNAMAX_RANDOM_STATUS_2:
        effect = status_list[Random() & 1];
        break;
    case DYNAMAX_GOLD_RUSH:
        gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 200);
        effect = MOVE_EFFECT_CONFUSION;
        break;
    }

    gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect = effect;
}

void DynamaxSelectWeatherCmd()
{
    u8 arg = gBattleMoves[gCurrentMove].argument;
    const static u8 weather_string[] = {0, 4, 3, 5};
    if (arg <= DYNAMAX_SET_HAIL && TryChangeBattleWeather(gBattlerAttacker, arg + 1, FALSE))
        gBattleCommunication[MULTISTRING_CHOOSER] = weather_string[arg];
    else
        gBattlescriptCurrInstr += 13;
}



void BattleScriptPushCurrent(const void* newPtr)
{
    BattleScriptPushCursor();
    gBattlescriptCurrInstr = newPtr;
}

void SetGMaxFieldData(u8 type)
{
    if (gBattleStruct->mega.gMaxFieldType != type || gBattleStruct->mega.gMaxFieldCounter == 0)
    {
        gBattleStruct->mega.gMaxFieldCounter = 4;
        gBattleStruct->mega.gMaxFieldType = type;
    }
}

void SetGiaFieldDataCmd()
{
    SetGMaxFieldData(gBattleMoves[gCurrentMove].argument);
}

void SetMoveEffect2()
{
    if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
        return;

	if (gBattleScripting.moveEffect & MOVE_EFFECT_AFFECTS_USER) {
		gBattleScripting.moveEffect &= ~MOVE_EFFECT_AFFECTS_USER;
		gEffectBattler = gBattlerAttacker;
		gBattleScripting.battler = gBattlerTarget; // theoretically the attacker
	} else {
		gEffectBattler = gBattlerTarget;
        gBattleScripting.battler = gBattlerAttacker;
	}
    switch (gBattleScripting.moveEffect)
    {
    case DYNAMAX_AURORA_VEIL:
        BattleScriptPushCurrent(BattleScript_DynamaxPrintAuroraVeil);
        break;
    case DYNAMAX_ATTRACT:
		BattleScriptPushCurrent(BattleScript_DynamaxTryinfatuating);
        break;
    case DYNAMAX_RECYCLE_ITEM:
        BattleScriptPushCurrent(BattleScript_DynamaxRecycleItem);
        break;
    case DYNAMAX_TEALTH_ROCK:
        BattleScriptPushCurrent(BattleScript_DynamaxSetTealthrock);
        break;
    case DYNAMAX_CLEAR_ROCK:
        HandleClearRocks();
        break;
    case DYNAMAX_HEAL_USER:
        if (gBattleMons[gBattlerAttacker].maxHP != gBattleMons[gBattlerAttacker].hp)
		{
			gBattleMoveDamage = gBattleMons[gEffectBattler].hp - gBattleMons[gEffectBattler].maxHP;
			BattleScriptPushCurrent(BattleScript_DynamaxHealSelfAll);
		}
        break;
    case DYNAMAX_PP_REDUCE:
        BattleScriptPushCurrent(BattleScript_DynamaxTryppreduce);
        break;
    case DYNAMAX_GRAVITY:
        break;
    case DYNAMAX_HEAL_PARTY_STATUS:
        BattleScriptPushCurrent(BattleScript_DynamaxHealPartyStatus);
        break;
    case DYNAMAX_TORMENT:
        BattleScriptPushCurrent(BattleScript_DynamaxSetTorment);
        break;
    case DYNAMAX_GMAX_1:
        BattleScriptPushCurrent(BattleScript_DynamaxEffectNotDoneYet);
        break;
    }
    gBattleScripting.moveEffect = 0;
}

bool32 HandleDynamaxEndTurnEffect()
{
    bool32 ret;
    struct BattleEvolutionData* mega;
    mega = &gBattleStruct->mega;
    ret = FALSE;
    switch (mega->gMaxEndTurnTracer)
    {
    case 0://revert
        ret = BattleEndTurnFormChange(gActiveBattler);
        gBattleStruct->mega.gMaxEndTurnTracer++;
        break;
    case 1://fieldx
        gBattleStruct->mega.choosenZmove[gBattlerAttacker] = 0;
        gBattleStruct->mega.gMaxEndTurnTracer = 0;
        gBattleStruct->turnEffectsTracker++;
        break;
    }

    return ret;
}


static void UndoDynamaxEvolution(u8 side, u32 monId)//end battle
{
    //极巨化后不可主动退场，AI也不会退场。
    //不需要知道具体的battler 只需要知道side即可
    if (DisableBattlerEvolution(side, monId, EvolutionDynamax))
    {
        gPlayerParty[monId].maxHP /= 2;
        gPlayerParty[monId].hp /= 2;
        gBattleStruct->mega.timer[gActiveBattler] = 0;
        gBattleSpritesDataPtr->battlerData[gActiveBattler].transformSpecies = 0;
        gBattleSpritesDataPtr->battlerData[gActiveBattler].dynamax = 0;
    }
}

void BattlerRecordUseItem(u8 battler)
{
     gLastUsedItem = gBattleMons[battler].item;
     RecordItemEffectBattle(battler, GetBattlerHoldEffect(battler, FALSE));
}

const struct Evolution* FindBattlerEvolution(u16 species, u16 type)
{
    u8 i;
    for (i = 0; i < EVOS_PER_MON; i++)
    {
        if (gEvolutionTable[species][i].method == type)
            return &gEvolutionTable[species][i];
    }
    return 0;
}

static void DoMegaEvolution(u32 battlerId)
{
    gLastUsedItem = gBattleMons[battlerId].item;
    if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY && GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
        gBattleCommunication[MULTISTRING_CHOOSER] = 2;
    else
        gBattleCommunication[MULTISTRING_CHOOSER] = gBattleMons[battlerId].species == SPECIES_RAYQUAZA ? 1 : 0;
    BattleScriptExecute(BattleScript_MegaEvolution);
}

u16 CalcMaxMove(const struct Evolution *evolution, u16 move)
{
    u32 j;
//    const struct Evolution *evolution = FindBattlerEvolution(gBattleMons[battlerId].species, EVO_DYNAMAX);
    u8 type = gBattleMoves[move].type;
    if (evolution != 0 && evolution->param2 == type)
    {
        return evolution->param;
    }
    else if (gBattleMoves[move].split == SPLIT_STATUS)
        return MOVE_MAX_GUARD;
    else for(j = MOVE_MAX_GUARD + 1;j <= MOVE_MAX_STEELSPIKE;j++)
    {
        if (type == gBattleMoves[j].type)
        {
            break;
        }
    }
    return j;
}

void GigaMaxReplaceMove(u32 battlerId)
{
     u32 i;
     const struct Evolution *evolution = FindBattlerEvolution(gBattleMons[battlerId].species, EVO_DYNAMAX);
     for(i = 0; i< MAX_MON_MOVES;i ++)
     {
         gBattleMons[battlerId].moves[i] = CalcMaxMove(evolution, gBattleMons[battlerId].moves[i]);
     }
    gChosenMoveByBattler[battlerId] = gBattleMons[battlerId].moves[gBattleStruct->chosenMovePositions[battlerId]];
}

u8 GetDynamaxMovePower(u8 battlerId)
{
    u16 originMove = GetMonData(GetBankPartyData(battlerId),MON_DATA_MOVE1 + gBattleStruct->chosenMovePositions[battlerId], 0);
    
    u16 dynamaxPower = gDynamaxMovePowers[originMove];
    u8 type = gBattleMoves[originMove].type;
    if (type == TYPE_FIGHTING || type == TYPE_POISON)
        dynamaxPower = dynamaxPower * 3 / 4;
    return dynamaxPower;
}

void DynamaxUpdateHpAndCounter()
{
    struct Pokemon* mon = GetBankPartyData(gActiveBattler);
    gBattleStruct->mega.timer[gActiveBattler] = 3;
}

static void DoDynamaxEvolution(u32 battlerId)
{
    if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) //玩家选择技能的时候就已经导入了
        GigaMaxReplaceMove(battlerId);
    gBattleCommunication[MULTISTRING_CHOOSER] = 0;
    DynamaxUpdateHpAndCounter();
    //GigaMaxReplaceMove(battlerId);
    BattleScriptExecute(BattleScript_Dynamax);
}


void KingChangeReplaceMove()
{
    u8 j;
    u16 move = ItemId_GetArg1(gBattleMons[gBattlerAttacker].item);
    u16 targetMove = ItemId_GetArg2(gBattleMons[gBattlerAttacker].item);
    for (j = 0; j < MAX_MON_MOVES; j++)
    {
        if (move == gBattleMons[gBattlerAttacker].moves[j])
        {
            struct Pokemon* mon = GetBankPartyData(gBattlerAttacker);
            gBattleMons[gBattlerAttacker].moves[j] = targetMove;
            gBattleMons[gBattlerAttacker].pp[j] /= 3;
            SetMonData2(mon, MON_DATA_PP1+j,gBattleMons[gBattlerAttacker].pp[j]);
            SetMonData2(mon, MON_DATA_MOVE1+j,targetMove);
            break;
        }
    }
}

//入场携带道具的形态转换
bool32 BattleSwitchInFormChange(u32 battlerId)
{
    u16 species = gBattleMons[battlerId].species;
    const struct Evolution* evolution = FindBattlerEvolution(species, EVO_PRIMAL_EVOLUTION);
    if (evolution == 0)
          evolution = FindBattlerEvolution(species, EVO_BATTLE_SWITCH_IN);
    if (evolution != 0 && gBattleMons[battlerId].item == evolution->param)
    {
        static const u8* const battleScripts[] = {BattleScript_Primal, BattleScript_KingChange};
        gBattleMons[battlerId].species = evolution->targetSpecies;
        gBattlerAttacker = battlerId;
        BattlerRecordUseItem(battlerId);
        BattleScriptExecute(battleScripts[ItemId_GetBattleUsage(gBattleMons[battlerId].item)]);
        return 1;
    }
    return 0;
}

static void PreCalMonEvolutionType(u8 battlerId)
{
    if (GetEvolutionType(battlerId) == 0)
        SetEvolutionType(battlerId, CalEvolutionType(battlerId));
}

bool8 PlayerHandleDisplayEvolutionSymbol()
{
    u8 type;
    gBattleStruct->mega.playerSelect = FALSE;
    if (!IsMegaTriggerSpriteActive())
        gBattleStruct->mega.triggerSpriteId = 0xFF;
    PreCalMonEvolutionType(gActiveBattler);
    type = GetEvolutionType(gActiveBattler);
    if (type != 0 && !IsEvolutionHappened(gActiveBattler, type))
        CreateTriggerSprite(gActiveBattler, type - 1);
    return type;
}

u8 OpponentPreHandleMegaEvo(u8 battlerId)
{
    u8 type;
    PreCalMonEvolutionType(battlerId);
    type = GetEvolutionType(battlerId);
    if (IsEvolutionHappened(battlerId, type))
        return 0;
    return type;
}


bool32 BattleBeforeUseMoveFormChange(u32 battlerId)
{
    switch (GetEvolutionType(battlerId)) {
        case EvolutionMega:
            DoMegaEvolution(battlerId);
            break;
        case EvolutionDynamax:
            DoDynamaxEvolution(battlerId);
            break;
        case EvolutionZmove:
            DoZMove(battlerId);
            break;
        default:
            return 0;
    }
    return 1;
}

bool32 TryAegiFormChange(void);
bool8 TryLegendaryFormChange();
bool32 BattlerUseMoveFormChange(u32 battlerId)
{
    return TryAegiFormChange() || TryLegendaryFormChange();
}

bool8 BattleEndTurnFormChange(u32 battlerId)
{
   return DynamaxUpdateEndturn(battlerId);
}


void BattleSwitchoutFormChange(u32 monId, u32 side, enum BattleSwitchoutType type)
{
    UndoFormChange(monId, side);
//    if (side == B_SIDE_PLAYER)
    UndoDynamaxEvolution(side, monId);
    if ((type == BATTLE_FAINT || type == BATTLE_END) && side == B_SIDE_PLAYER)
    {
        UndoSwitchInFormChange(monId);
        UndoMegaEvolution(monId);
    }
    if (type == BATTLE_END && side == B_SIDE_PLAYER)
    {
        TryBurmmyChange(monId);
    }
}


